﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System.Runtime.Serialization;
namespace Rogue
{
    // The end boss - he's just a regular goblin with more health and meaner attacks
    [Serializable]
    public class KingGoblin : Goblin
    {
        public KingGoblin(string a_sName) : base(a_sName)
        {
        }

        

        #region Serialization
        public KingGoblin(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }
        #endregion
    }

    [Serializable]
    public class Goblin : Entity
    {
        public Goblin(string a_sName)       // the named goblin is the king. Kind of a hack...
            : base(a_sName)
        {
            m_bKing = true;
            AddGoblinBody();
        }

        public Goblin()
            : base()
        {
            AddGoblinBody();

            if (Random.Instance.Next(10) < 2)
            {
                m_Club = new Weapon("knife", s_Slash);
                PickUp(m_Club);
            }
            else if (Random.Instance.Next(10) < 4)
            {
                m_Club = new Weapon("cudgel", s_Club);
                PickUp(m_Club);
            }
            else
            {
                m_Club = null;
            }
        }

        Weapon m_Club;

        // we don't give the kick attack to the Leg class, because only some legged creatures should kick
        static Attack s_Bite = new Attack("bite", "bites", "mouth", 20, DamageType.Cut, Attack.Priority.High);
        static Attack s_Slash = new Attack("slash", "slashes", "knife", 20, DamageType.Cut, Attack.Priority.High);
        static Attack s_Club = new Attack("club", "clubs", "cudgel", 12, DamageType.Bash, Attack.Priority.High); 
        static Attack s_Punch = new Attack("punch", "punches", "arms", 7, DamageType.Bash, Attack.Priority.Normal);
        static Attack s_Kick = new Attack("kick", "kicks", "legs", 5, DamageType.Bash, Attack.Priority.Low);

        bool m_bKing = false;

        private void AddGoblinBody()
        {
            m_BodyRoot = new Torso(this);
            Head head = new Head(m_BodyRoot);
            Arm leftArm = new Arm(m_BodyRoot, "left");
            Arm rightArm = new Arm(m_BodyRoot, "right");
            rightArm.GrantedAttack = s_Punch;
            leftArm.GrantedAttack = s_Punch;
            rightArm.OnSubPartLoss += new BodyPart.PartLossHandler(armLoss);
            leftArm.OnSubPartLoss += new BodyPart.PartLossHandler(armLoss);

            Leg leftLeg = new Leg(m_BodyRoot, "left");
            Leg rightLeg = new Leg(m_BodyRoot, "right");
            leftLeg.GrantedAttack = s_Kick;
            rightLeg.GrantedAttack = s_Kick;

            if (m_bKing)
            {
                head.GrantedAttack = s_Bite;
                m_BodyRoot.MultMaxHealth(1.2f);
            }
            else
            {
                m_BodyRoot.MultMaxHealth(0.6f);
            }

            m_BodyRoot.OnSubPartLoss += SubPartLossHandler;
            m_BodyRoot.OnSubPartHealthChange += BodyHealthChange;

            m_BodyRoot.InitAttackIndices();
        }

        static bool s_ShowedWeaponsTut = false;

        void armLoss(BodyPart a_Part)
        {
            if (m_BodyRoot.GetNumParts<Arm>() == 0)
            {
                Drop(m_Club);
                ShowWeaponsTut(m_Club);
            }
        }

        // this is what can kill us.
        private void SubPartLossHandler(BodyPart a_Part)
        {
            if (a_Part.GetType() == typeof(Head) || a_Part.GetType() == typeof(Torso))
            {
                m_IsAlive = false;

                Drop(m_Club);
                ShowWeaponsTut(m_Club);
            }

            a_Part.MakeSeveredPart(this);
        }

        void BodyHealthChange(BodyPart a_Part, BodyPart.HealthChangeType a_Type, int a_DeltaHealth)
        {
            if (a_Type == BodyPart.HealthChangeType.Bleed)
            {
                RogueGame.Instance.Map.BloodyTerrain(X, Y, true);
            }
        }

        protected void ShowWeaponsTut(Entity a_Club)
        {
            if (!s_ShowedWeaponsTut && a_Club != null)
            {
                string msg = "To pick up a weapon, stand on it and hit P.";
                TutWindow.Instance.Show("Weapons", msg, 3.0f, a_Club);

                Entity hero = RogueGame.Instance.Map.GetHero();
                hero.OnPickup += new PickupHandler(hero_OnPickup);
                
                s_ShowedWeaponsTut = true;
            }            
        }

        void hero_OnPickup(Entity.PickupArgs e)
        {
            string msg = "When there are multiple attacks you can do (ie. once you pick up a weapon), select attacks with the F keys.\n\n" + 
                "When an attack is selected, it will be used next time you attack an enemy by bumping into them, or attacking them via the Enemy window (E).\n\n" + 
                "To see this Hero window (which also shows you your health), press W.";

            Tutorial t = TutWindow.Instance.Show("Attack Selection", msg, 10.0f, DetailWindow.HeroWindow);
            t.m_Position = new Point(TutWindow.Instance.Position.X, 400);

            Entity hero = RogueGame.Instance.Map.GetHero();
            hero.OnPickup -= hero_OnPickup;
            hero.OnMove += new MoveHandler(hero_OnMoveAfterPickup);

            DetailWindow.HeroWindow.Focus(hero, true);
        }

        void hero_OnMoveAfterPickup(Entity.MoveArgs e)
        {
            Entity hero = RogueGame.Instance.Map.GetHero();
            hero.OnMove -= hero_OnMoveAfterPickup;
        }

        static DetailWindowImages s_GoblinDetailWindowImages = new DetailWindowImages("Goblin");
        public override DetailWindowImages GetDetailWindowImages()
        {
            return s_GoblinDetailWindowImages;
        }

        public override int GetMoveDelay()
        {
            int numLegs = m_BodyRoot.GetNumParts<Leg>();
            switch (numLegs)
            {
                case 0: return m_nDelay * 10;
                case 1: return m_nDelay * 2;
                case 2: return m_nDelay;
            }

            return 1 + (m_nDelay - 1) / numLegs;
        }

        int insultLabel = -1;

        double m_TimeOfLastInsult = 0.0f;

        public override void Think()
        {
            Entity hero = RogueGame.Instance.Map.GetHero();

            int dx = hero.X - X;
            int dy = hero.Y - Y;
            
            if (hero.IsAlive && Math.Abs(dx) <= 1 && Math.Abs(dy) <= 1)    // close enough to attack?
            {
                MeleeAttack(hero);
            }
            else
            {
                if (!hero.IsAlive || !RogueGame.Instance.Map.LightCaster.IsVisible(Position))
                {
                    double timeSinceInsult = (Timeline.Instance.LastGameTime.TotalRealTime.TotalMilliseconds - m_TimeOfLastInsult) / 1000.0f;

                    if (m_bKing && Random.Instance.Next(10) > 4 && timeSinceInsult > 3.0f)
                    {
                        string[] insults = {"I can smell you!", "I'm going to bite you!", "I will find you!", "I am the Goblin King!"};
                        string insult = insults[Random.Instance.Next(insults.Length)];

                        if (!hero.IsAlive)
                        {
                            insult = "Ha!";
                        }

                        insultLabel = LabelHelper.Instance.Add(insult, this, 2.0f);
                        m_TimeOfLastInsult = Timeline.Instance.LastGameTime.TotalRealTime.TotalMilliseconds;
                    }

                    dx = Random.Instance.Next(-1, 2);
                    dy = Random.Instance.Next(-1, 2);
                }

                // hacky until we get pathfinding. This should get entities to dodge around trees OK
                int sx = Math.Sign(dx);
                int sy = Math.Sign(dy);
                Direction[] tryMoving = {
                    Direction.GetDirection(sx, sy),
                    Direction.GetDirection(sx+1, sy),
                    Direction.GetDirection(sx, sy+1),
                    Direction.GetDirection(sx-1, sy),
                    Direction.GetDirection(sx, sy-1),
                };

                foreach (Direction d in tryMoving)
                {
                    if (d == null)  // if it's not a valid direction
                        continue;

                    // we're free to move there
                    Point newPos = Position + d;

                    // do any entities block us?
                    bool breakOuter = false;
                    foreach (Entity e in Entity.Collector.GetAllAt<Entity>(newPos.X, newPos.Y))
                    {
                        if (e.BlocksActor(this))
                        {
                            breakOuter = true;
                        }
                    }
                    if (breakOuter)
                        break;

                    // does the terrain block us?
                    if (!RogueGame.Instance.Map.BlocksActor(newPos.X, newPos.Y))
                    {
                        MoveDelta(d);
                        break;
                    }
                }
            }

            // call this here in case we didn't pick another action.
            if (m_nNextThink == Timeline.Instance.CurrentTime)
                EndTurn();

            bool wasAlive = IsAlive;

            // this is kind of weird, we're pre-healing.
            // It should be m_nNextThink - m_nLastThink...
            int elapsedTime = m_nNextThink - Timeline.Instance.CurrentTime;
            m_BodyRoot.UpdateSubpartsHealth(elapsedTime);

            if (wasAlive && !IsAlive)
            {
                // TODO: have the battle reporter thing handle this
                ConsoleWindow.Instance.AddMessage(String.Format("{0} has bled to death.", Name), Color.Red);
                Die();
            }
        }

        #region Serialization
        public Goblin(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            InputManager.Instance.OnKeyPressed += new InputManager.OnKeyPressedHandler(OnKeyPressed);
        }

        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
        }
        #endregion
    }
}


